#include "QRenderTransferFunction1DView.h"
#include "QRenderHoverpoints.h"

QRenderTransferFunction1DView::QRenderTransferFunction1DView(QWidget *parent)
: QWidget(parent), m_alpha_gradient_(QLinearGradient(0, 0, 0, 0))
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
    QPolygonF points;
    points << QPointF(0, sizeHint().height()) << QPointF(sizeHint().width(), 0.0f);
    m_hoverPoints_ = new QRenderHoverPoints(this, QRenderHoverPoints::CircleShape);
    m_hoverPoints_->setConnectionType(QRenderHoverPoints::LineConnection);
    m_hoverPoints_->setPoints(points);
    m_hoverPoints_->setPointLock(0, QRenderHoverPoints::LockToLeft);
    m_hoverPoints_->setPointLock(1, QRenderHoverPoints::LockToRight);
    m_hoverPoints_->setSortType(QRenderHoverPoints::XSort);
	
    
    for (int i = 0; i < NUMBER_INT_ENTRIES; ++i)
    {
        regionPixelNumber_[i] = i;
    }
    maximumPixelNumber_ = NUMBER_INT_ENTRIES - 1;
    
    connect(m_hoverPoints_, SIGNAL(oneDTFChanged()), this, SLOT(updateTransferFunction()));

    histogramData_ = NULL;
    histogramSize_ = 0;
    visHistogramData_ = NULL;
}

void QRenderTransferFunction1DView::drawVisHistogram()
{
    if (visHistogramData_ == NULL) return;

    ::size_t visHistogramSize = visHistogramSize_;
    float* visHistogramData = visHistogramData_;

    // Generate the histogram path
    qreal scale = this->height() * 0.8f;
    qreal stepSize = (float)this->width() / visHistogramSize;

    QPainterPath* visHistogramPath = new QPainterPath();
    visHistogramPath->moveTo(0.0, 0.0);
    visHistogramPath->lineTo(0.0, this->height() - *visHistogramData * scale);

    qreal x = -stepSize * 0.5, y = 0.0;
    for(int i = 0; i < visHistogramSize; i++)
    {
        x += stepSize;
        y = this->height() - *(visHistogramData++) * scale;
        visHistogramPath->lineTo(x, y);
    }
    visHistogramPath->lineTo(this->width(), visHistogramPath->currentPosition().y());
    visHistogramPath->lineTo(this->width(), 0.0);
    visHistogramPath->lineTo(0.0, 0.0);

    QPainter painter(this);
    QColor color(200, 200, 200, 125);
    painter.setBrush(color);
    painter.setPen(Qt::red);
    painter.setRenderHints(QPainter::Antialiasing);
    painter.drawPath(*visHistogramPath);
    painter.end();
}

void QRenderTransferFunction1DView::drawHistogram()
{
    if (histogramData_ == NULL) return;

    ::size_t histogramSize = histogramSize_;
    float* histogramData = histogramData_;

    // Generate the histogram path
    qreal scale = this->height() * 0.8f;
    qreal stepSize = (float)this->width() / histogramSize;

    QPainterPath* histogramPath = new QPainterPath();
    histogramPath->moveTo(0.0, 0.0);
    histogramPath->lineTo(0.0, this->height() - *histogramData * scale);

    qreal x = -stepSize * 0.5, y = 0.0;
    for(int i = 0; i < histogramSize; i++)
    {
        x += stepSize;
        y = this->height() - *(histogramData++) * scale;
        histogramPath->lineTo(x, y);
    }
    histogramPath->lineTo(this->width(), histogramPath->currentPosition().y());
    histogramPath->lineTo(this->width(), 0.0);
    histogramPath->lineTo(0.0, 0.0);

    QPainter painter(this);
    QColor color(200, 200, 200, 125);
    painter.setBrush(color);
    painter.setPen(Qt::blue);
    painter.setRenderHints(QPainter::Antialiasing);
    painter.drawPath(*histogramPath);
    painter.end();
}


void QRenderTransferFunction1DView::drawColorBar()
{
    QPolygonF *points = &m_hoverPoints_->points();
    if (points->size() < 2) return;

    QPainterPath* hoverPointPath = new QPainterPath();
    hoverPointPath->moveTo(0.0f, this->height());
    for (QVector<QPointF>::iterator i = points->begin();  i != points->end(); i++)
    {
        hoverPointPath->lineTo(*i);
    }
    hoverPointPath->lineTo(this->width(), this->height());

    //create the linear gradient.
    QLinearGradient linearGradient;
    this->generateLinearGradient(linearGradient);

    QPainter painter(this);
    painter.setBrush(linearGradient);
    painter.setPen(Qt::darkGray);
    painter.setRenderHints(QPainter::Antialiasing);
    painter.drawPath(*hoverPointPath);
    painter.end();
}

void QRenderTransferFunction1DView::save()
{
	m_hoverPoints_->save(this->size());
}

void QRenderTransferFunction1DView::load()
{
	m_hoverPoints_->load(this->size());
	emit colorsChanged(getColorArray());
	update();
}

void QRenderTransferFunction1DView::drawHistogramBackground()
{
    // Generate the histogram path
    float heightTemp = this->height();
    float stepTemp = (float)this->width() / NUMBER_INT_ENTRIES;
    
    QPainterPath* histogramOutlinePath = new QPainterPath();
    float preX, preY, curX, curY, nextX, nextY;
    preX = 0.0;
    preY = (1 - (float)regionPixelNumber_[0] / maximumPixelNumber_ * 0.8f) * heightTemp;
    histogramOutlinePath->moveTo(preX, preY);
    for( int i = 1; i < NUMBER_INT_ENTRIES; ++i )
    {
        curX = i * stepTemp;
        curY = (1 - (float)regionPixelNumber_[i] / maximumPixelNumber_ * 0.8f) * heightTemp;
        nextX = curX + stepTemp;
        nextY = (1 - (float)regionPixelNumber_[i + 1] / maximumPixelNumber_ * 0.8f) * heightTemp;
        histogramOutlinePath->cubicTo(preX, preY, curX, curY, nextX, nextY);

        preX = curX;
        preY = curY;
        curX = nextX;
        curY = nextY;
    }
    histogramOutlinePath->lineTo( histogramOutlinePath->currentPosition().x(), heightTemp );
    histogramOutlinePath->lineTo( 0.00, heightTemp );
    histogramOutlinePath->lineTo( 0.0, (1 - (float)regionPixelNumber_[0] / maximumPixelNumber_ * 0.8f) * heightTemp);
    
    QPainter tempPainter(this);
    QColor color(200, 200, 200, 125);
    tempPainter.setBrush(color);
    tempPainter.setPen(Qt::gray);
    tempPainter.setRenderHints(QPainter::Antialiasing);
    tempPainter.drawPath(*histogramOutlinePath);
    tempPainter.end();
}

void QRenderTransferFunction1DView::generateLinearGradient(QLinearGradient &linearGradient)
{
    float width = this->width();
    linearGradient.setStart( 0.00, 0.00);
    linearGradient.setFinalStop(this->width(), 0.00);
    
    for (int i = 0; i < hoverPoints()->points().size(); ++i)
    {
        linearGradient.setColorAt(hoverPoints()->points().at(i).x() / width, hoverPoints()->getRepresentColor().at(i));
    }
}

void QRenderTransferFunction1DView::drawColorBackground()
{
    if (hoverPoints()->points().size() < 2) return;
    
    QPainterPath* hoverPointPath = new QPainterPath();
    hoverPointPath->moveTo(0.0f, this->height());
    for (int i = 0;  i < hoverPoints()->points().size(); ++i)
        hoverPointPath->lineTo(hoverPoints()->points().at(i));
    hoverPointPath->lineTo(this->width(), this->height());
    
    //create the linear gradient.
    QLinearGradient linearGradient;
    this->generateLinearGradient(linearGradient);
    
    QPainter tempPainter(this);
    tempPainter.setBrush(linearGradient);
    tempPainter.setPen(Qt::darkGray);
    tempPainter.setRenderHints(QPainter::Antialiasing);
    tempPainter.drawPath(*hoverPointPath);
    tempPainter.end();
}

void QRenderTransferFunction1DView::updateBackground()
{
    //drawHistogramBackground();
    drawColorBackground();
}

QPolygonF QRenderTransferFunction1DView::points() const
{
    return m_hoverPoints_->points();
}

void QRenderTransferFunction1DView::paintEvent(QPaintEvent *)
{
    updateBackground();
    drawHistogram();
    drawVisHistogram();
}

float* QRenderTransferFunction1DView::getColorArray()
{
    QPolygonF *points = &m_hoverPoints_->points();
    QVector<QColor> *colors = &m_hoverPoints_->getRepresentColor();
    float width = (float)this->width() / NUMBER_TF_ENTRIES;
    float x = width * 0.5f;
    int index = 0;
    QPointF *p = (QPointF *)&points->at(0);
    for (int i = 0; i < NUMBER_TF_ENTRIES; ++i)
    {
		
        while (index < points->size()-2 && points->at(index + 1).x() <= x ) index++;
        float rate = float(x - points->at(index).x()) / (points->at(index + 1).x() - points->at(index).x());
        regionColor_[i * 4 + 0] = colors->at(index).redF()   * (1 - rate) + colors->at(index + 1).redF()   * rate;
        regionColor_[i * 4 + 1] = colors->at(index).greenF() * (1 - rate) + colors->at(index + 1).greenF() * rate;
        regionColor_[i * 4 + 2] = colors->at(index).blueF()  * (1 - rate) + colors->at(index + 1).blueF()  * rate;
        regionColor_[i * 4 + 3] = 1.0f - (points->at(index).y() * (1 - rate) + points->at(index + 1).y()  * rate) / this->height();
        x += width;
    }
    
    return regionColor_;
}

void QRenderTransferFunction1DView::setRegionPixelArray(int* regionPixels)
{
    maximumPixelNumber_ = 0;
    for (int i = 0; i < NUMBER_INT_ENTRIES; ++i)
    {
        regionPixelNumber_[i] = regionPixels[i];
        if (regionPixelNumber_[i] > maximumPixelNumber_)
            maximumPixelNumber_ = regionPixelNumber_[i];
    }
}

void QRenderTransferFunction1DView::updateTransferFunction()
{
    emit colorsChanged(getColorArray());
}

void QRenderTransferFunction1DView::slotUpdateHistogram(float* histogramData, ::size_t histogramSize)
{
    histogramSize_ = histogramSize;
    histogramData_ = histogramData;
    update();
}

void QRenderTransferFunction1DView::slotUpdateVisHistogram(float* visHistogramData, ::size_t visHistogramSize)
{
    visHistogramSize_ = visHistogramSize;
    visHistogramData_ = visHistogramData;
    update();
}